package org.sun.onepink.utils

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import androidx.annotation.RequiresPermission
import androidx.core.content.ContextCompat
import java.io.ByteArrayOutputStream
import java.util.concurrent.atomic.AtomicBoolean

class AudioRecorder(private val context: Context) {
    private var recorder: AudioRecord? = null
    private val isRecording = AtomicBoolean(false)
    private lateinit var outputStream: ByteArrayOutputStream
    private val bufferSize: Int
    private var recordingThread: Thread? = null

    init {
        bufferSize = AudioRecord.getMinBufferSize(
            16000,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT
        ).coerceAtLeast(1024)
    }

    @Throws(
        SecurityException::class,
        IllegalStateException::class,
        AudioRecordInitException::class
    )
    fun startRecording(maxDurationMillis: Long = 0) {
        checkPermission()
        checkNotRecording()

        outputStream = ByteArrayOutputStream()
        recorder = createAudioRecord() ?: throw AudioRecordInitException()

        isRecording.set(true)
        recorder!!.startRecording()

        recordingThread = Thread({
            val buffer = ByteArray(bufferSize)
            var totalDuration: Long = 0
            val startTime = System.currentTimeMillis()

            while (isRecording.get()) {
                if (maxDurationMillis > 0 && totalDuration >= maxDurationMillis) {
                    break
                }

                val bytesRead = recorder!!.read(buffer, 0, bufferSize)
                if (bytesRead > 0) {
                    outputStream.write(buffer, 0, bytesRead)
                    totalDuration = System.currentTimeMillis() - startTime
                } else if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) {
                    throw IllegalStateException("Invalid operation when reading audio data")
                } else if (bytesRead == AudioRecord.ERROR_BAD_VALUE) {
                    throw IllegalStateException("Bad value when reading audio data")
                }
            }
        }, "AudioRecorder-Thread").apply { start() }
    }

    @Throws(IllegalStateException::class)
    fun stopRecording(): ByteArray {
        if (!isRecording.get()) {
            throw IllegalStateException("Not recording")
        }

        isRecording.set(false)
        recordingThread?.join(500) // 等待录音线程结束
        recordingThread = null

        recorder?.apply {
            try {
                stop()
            } catch (e: IllegalStateException) {
                // 可能已经停止，忽略
            }
            release()
        }
        recorder = null

        return outputStream.toByteArray().also {
            try {
                outputStream.close()
            } catch (e: Exception) {
                // 忽略关闭异常
            }
        }
    }

    fun isRecording(): Boolean = isRecording.get()

    fun release() {
        if (isRecording.get()) {
            try {
                stopRecording()
            } catch (e: Exception) {
                // 忽略停止异常
            }
        }
    }

    private fun checkPermission() {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED) {
            throw SecurityException("Recording permission denied")
        }
    }

    private fun checkNotRecording() {
        if (isRecording.get()) {
            throw IllegalStateException("Already recording")
        }
    }

    @RequiresPermission(Manifest.permission.RECORD_AUDIO)
    private fun createAudioRecord(): AudioRecord? {
        return AudioRecord(
            MediaRecorder.AudioSource.VOICE_RECOGNITION,
            16000,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            bufferSize
        ).takeIf { it.state == AudioRecord.STATE_INITIALIZED }
    }

    class AudioRecordInitException : Exception("Failed to initialize AudioRecord")
}